package com.zzyl.nursing.service.impl;


import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;

import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.huaweicloud.sdk.iotda.v5.model.AuthInfo;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;

import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.service.IDeviceDataService;
import com.zzyl.nursing.vo.DeviceDetailVo;
import com.zzyl.nursing.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.service.IDeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;

/**
 * 设备Service业务层处理
 *
 * @author szc
 * @date 2025-08-21
 */
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private IoTDAClient ioTDAClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IoTDAClient iotClient;

    @Autowired
    private IDeviceDataService deviceDataService;

    /**
     * 查询设备
     *
     * @param id 设备主键
     * @return 设备
     */
    @Override
    public Device selectDeviceById(Long id) {
        return deviceMapper.selectById(id);
    }

    /**
     * 查询设备列表
     *
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return deviceMapper.insert(device);
    }

    /**
     * 修改设备
     *
     * @param deviceDto 设备
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateDevice(DeviceDto deviceDto) {
        Device device = new Device();
        Device dbDevice = this.lambdaQuery().eq(Device::getIotId, deviceDto.getIotId()).one();
        if (!deviceDto.getDeviceName().equals(dbDevice.getDeviceName()) && isExistName(deviceDto.getDeviceName())) {
            throw new BaseException("设备名称已存在");
        }
        //同样位置是否绑定了相同产品
        //位置是否改变
        if (deviceDto.getPhysicalLocationType() == null) {
            deviceDto.setPhysicalLocationType(-1);
        }
        boolean flag = dbDevice.getBindingLocation().equals(deviceDto.getBindingLocation().toString())
                && dbDevice.getPhysicalLocationType().equals(deviceDto.getPhysicalLocationType())
                && dbDevice.getLocationType().equals(deviceDto.getLocationType());
        if (!flag && isLocatedProduct(deviceDto)) {
            throw new BaseException("该位置已绑定其他产品");
        }
        UpdateDeviceRequest updateDeviceRequest = new UpdateDeviceRequest();
        updateDeviceRequest.setDeviceId(deviceDto.getIotId());
        UpdateDevice updateDevice = new UpdateDevice();
        updateDevice.setDeviceName(deviceDto.getDeviceName());
        updateDeviceRequest.setBody(updateDevice);
        try {
            UpdateDeviceResponse updateDeviceResponse = ioTDAClient.updateDevice(updateDeviceRequest);
            String nodeId = updateDeviceResponse.getNodeId();
            String deviceName = updateDeviceResponse.getDeviceName();
            device.setId(deviceDto.getId());
            device.setIotId(deviceDto.getIotId());
            device.setNodeId(nodeId);
            device.setBindingLocation(deviceDto.getBindingLocation().toString());
            device.setLocationType(deviceDto.getLocationType());
            device.setPhysicalLocationType(deviceDto.getPhysicalLocationType());
            device.setDeviceName(deviceName);
            device.setSecret(updateDeviceResponse.getAuthInfo().getSecret());
            device.setProductKey(deviceDto.getProductKey());
            device.setProductName(deviceDto.getProductName());
            device.setDeviceDescription(deviceDto.getDeviceDescription());
            device.setRemark(deviceDto.getRemark());
            if (device.getPhysicalLocationType().equals(-1)) {
                device.setHaveEntranceGuard(0);
            } else {
                device.setHaveEntranceGuard(1);
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw new BaseException("修改设备失败");
        }
        LambdaUpdateWrapper<DeviceData> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(DeviceData::getDeviceName, device.getDeviceName())
                .set(DeviceData::getDeviceDescription, device.getDeviceDescription())
                .set(DeviceData::getLocationType, device.getLocationType())
                .set(DeviceData::getPhysicalLocationType, device.getPhysicalLocationType())
                .set(DeviceData::getAccessLocation, device.getRemark())
                .eq(DeviceData::getIotId, deviceDto.getIotId());
        deviceDataService.update(wrapper);
        return deviceMapper.updateById(device);
    }

    /**
     * 批量删除设备
     *
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }

    @Override
    public void syncProductList() {
        ListProductsRequest request = new ListProductsRequest();
        ListProductsResponse listProductsResponse = ioTDAClient.listProducts(request);
        if (listProductsResponse.getHttpStatusCode() != 200) {
            throw new BaseException("同步产品列表失败");
        }
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCTS_KEY, JSONUtil.toJsonStr(listProductsResponse.getProducts()));
    }

    /**
     * 删除设备信息
     *
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id) {
        return deviceMapper.deleteById(id);
    }

    @Override
    public List<ProductVo> getAllProducts() {
        //从redis中获取products
        String products = (String) redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCTS_KEY);
        if (StrUtil.isBlank(products)) {
            return new ArrayList<>();
        }
        return JSONUtil.toList(products, ProductVo.class);
    }

    @Override
    public DeviceDetailVo getByIotId(String iotId) {
        Device device = this.lambdaQuery().eq(Device::getIotId, iotId)
                .one();
        if (Objects.isNull(device)) {
            throw new BaseException("设备不存在");
        }
        DeviceDetailVo deviceDetailVo = BeanUtil.toBean(device, DeviceDetailVo.class);
        //从华为云iot获取信息
        ShowDeviceRequest showDeviceRequest = new ShowDeviceRequest();
        showDeviceRequest.setDeviceId(iotId);
        ShowDeviceResponse showDeviceResponse = iotClient.showDevice(showDeviceRequest);
        deviceDetailVo.setDeviceStatus(showDeviceResponse.getStatus());
        String activeTime = showDeviceResponse.getActiveTime();
        if (StrUtil.isNotBlank(activeTime)) {
            LocalDateTime localDateTime = LocalDateTimeUtil.parse(activeTime, DatePattern.UTC_MS_PATTERN);
            ZonedDateTime utc = localDateTime.atZone(ZoneId.of("UTC"));
            ZonedDateTime zonedDateTime = utc.withZoneSameInstant(ZoneId.systemDefault());
            deviceDetailVo.setActiveTime(zonedDateTime.toLocalDateTime());
        }
        return deviceDetailVo;
    }

    /**
     * 根据iotId删除设备
     *
     * @param iotId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDeviceByIotId(String iotId) {
        //删除云端设备
        try {
            this.remove(new QueryWrapper<Device>().eq("iot_id", iotId));
            deviceDataService.remove(new QueryWrapper<DeviceData>().eq("iot_id", iotId));
            DeleteDeviceRequest request = new DeleteDeviceRequest();
            request.withDeviceId(iotId);
            DeleteDeviceResponse response = iotClient.deleteDevice(request);
            if (response.getHttpStatusCode() != 204) {
                System.out.println(response.getHttpStatusCode());
                throw new BaseException("iot删除设备失败");
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
            throw new BaseException("删除设备失败");
        }

    }

    @Override
    public List<Map<String, Object>> queryServiceProperties(String iotId) {
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(iotId);
        ShowDeviceShadowResponse response = iotClient.showDeviceShadow(request);
        if (response.getHttpStatusCode() != 200) {
            throw new RuntimeException("查询设备上报失败");
        }
        List<Map<String, Object>> list = new ArrayList<>();
        List<DeviceShadowData> shadow = response.getShadow();
        if (CollectionUtil.isEmpty(shadow)) {
            return list;
        }
        shadow.forEach(deviceShadowData -> {
            JSONObject jsonObject = JSONUtil.parseObj(deviceShadowData.getReported().getProperties());
            //处理上报时间日期
            LocalDateTime activeTime = LocalDateTimeUtil.parse(deviceShadowData.getReported().getEventTime(), "yyyyMMdd'T'HHmmss'Z'");
            //日期时区转换
            LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.systemDefault())
                    .toLocalDateTime();

            jsonObject.forEach((k, v) -> {
                Map<String, Object> map = new HashMap<>();
                map.put("functionId", k);
                map.put("value", v);
                map.put("eventTime", eventTime);
                list.add(map);
            });
        });

        return list;
    }

    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 1.设备名称是否重复
        if (isExistName(deviceDto.getDeviceName())) {
            throw new BaseException("设备名称【" + deviceDto.getDeviceName() + "】已存在");
        }
        // 2.设备标识是否重复
        if (isExistNodeId(deviceDto.getNodeId())) {
            throw new BaseException("设备标识【" + deviceDto.getNodeId() + "】已存在");
        }
        // 3.同一位置是否绑定了相同的产品
        if (Objects.isNull(deviceDto.getPhysicalLocationType())) {
            deviceDto.setPhysicalLocationType(-1);
        }
        if (isLocatedProduct(deviceDto)) {
            throw new BaseException("该位置已绑定相同产品");
        }

        // 4.注册设备到华为IOT
        AddDeviceResponse addDeviceResponse = saveDeviceToIot(deviceDto);

        // 5.本地保存设备
        saveDevice(deviceDto, addDeviceResponse);

    }

    public void saveDevice(DeviceDto deviceDto, AddDeviceResponse addDeviceResponse) {
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setIotId(addDeviceResponse.getDeviceId());
        device.setSecret(addDeviceResponse.getAuthInfo().getSecret());
        if (deviceDto.getPhysicalLocationType().equals(-1)) {
            device.setHaveEntranceGuard(0);
        } else {
            device.setHaveEntranceGuard(1);
        }
        save(device);
    }

    public AddDeviceResponse saveDeviceToIot(DeviceDto deviceDto) {
        AddDevice addDevice = new AddDevice();
        addDevice.setNodeId(deviceDto.getNodeId());
        addDevice.setDeviceName(deviceDto.getDeviceName());
        addDevice.setProductId(deviceDto.getProductKey());
        AuthInfo authInfo = new AuthInfo();
        authInfo.setAuthType("SECRET");
        authInfo.setSecret(UUID.randomUUID().toString().replace("-", ""));
        authInfo.setSecureAccess(Boolean.TRUE);
        addDevice.setAuthInfo(new AuthInfo());
        addDevice.setDescription(deviceDto.getDeviceDescription());
        AddDeviceRequest addDeviceRequest = new AddDeviceRequest();
        addDeviceRequest.withBody(addDevice);
        AddDeviceResponse addDeviceResponse = ioTDAClient.addDevice(addDeviceRequest);
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            throw new BaseException("注册设备失败");
        }
        return addDeviceResponse;
    }

    public boolean isLocatedProduct(DeviceDto deviceDto) {
        return this.lambdaQuery().eq(Device::getProductKey, deviceDto.getProductKey())
                .eq(Device::getLocationType, deviceDto.getLocationType())
                .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                .eq(Device::getBindingLocation, deviceDto.getBindingLocation())
                .count() > 0;
    }

    public boolean isExistNodeId(String nodeId) {
        return this.lambdaQuery().eq(Device::getNodeId, nodeId).count() > 0;
    }

    public boolean isExistName(String deviceName) {
        Long count = this.lambdaQuery().eq(Device::getDeviceName, deviceName).count();
        return count > 0;
    }

    @Override
    public List<ServiceCapability> queryProduct(String productKey) {
        ShowProductResponse showProductResponse = iotClient.showProduct(new ShowProductRequest().withProductId(productKey));
        return showProductResponse.getServiceCapabilities();
    }
}
